package my.ldap;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.*;

public class MyLdapCRUDTest {

    private static final String PROVIDER_URL = "ldap://39.106.152.6:389/"; // ldap服务器的ip地址
    private static final String LOGIN_DN = "cn=admin,dc=huada,dc=pcm";  // 登录DN
    private static final String PASSWORD = "admin";                     // 登录密码
    public static final String SECURITY_AUTHENTICATION = "simple";      // 验证的类型 "none", "simple", "strong"
    public static final String INITIAL_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; // 连接LDAP的实现工厂
    public static LdapContext ldapContext = null;

    /**
     * 获取LDAP连接
     *
     * @return ldap连接
     */
    public static LdapContext connectLdap() {
        Hashtable<String, Object> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
        env.put(Context.PROVIDER_URL, PROVIDER_URL);
        env.put(Context.SECURITY_AUTHENTICATION, SECURITY_AUTHENTICATION);
        env.put(Context.SECURITY_PRINCIPAL, LOGIN_DN);
        env.put(Context.SECURITY_CREDENTIALS, PASSWORD);
        try {
            ldapContext = new InitialLdapContext(env, null);
        } catch (NamingException e) {
            e.printStackTrace();
            System.out.println("用户" + LOGIN_DN + "登录验证失败");
            System.out.println("错误信息：" + e.getExplanation());
        }
        return ldapContext;
    }

    /**
     * 关闭LDAP连接
     */
    public static void close() {
        if (ldapContext != null) {
            try {
                ldapContext.close();
            } catch (NamingException e) {
                System.out.println("NamingException in close():" + e);
            }
        }
    }


    /**
     * 根据节点的DN查询其相关信息-测试1
     *
     * @param searchDN
     */
    @Deprecated
    public static void findByDN1(String searchDN) {
        LdapContext ldapContext = connectLdap();
        try {
            Attributes attributes = ldapContext.getAttributes(searchDN);
            NamingEnumeration<? extends Attribute> namingEnumeration = attributes.getAll();
            for (NamingEnumeration ae = attributes.getAll(); ae.hasMore(); ) {
                // 获取一个属性
                Attribute attr = (Attribute) ae.next();
                for (NamingEnumeration ve = attr.getAll(); ve.hasMore(); ) {
                    System.out.println(String.format("Attribute=%s,Value=%s", attr.getID(), ve.next()));
                }
            }
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据节点的DN查询其相关信息-测试2
     * <p>
     * 查询指定dn下所有的一级列表（我目前是这样理解的）
     * </p>
     *
     * @param searchDN
     */
    public static List findByDN2(String searchDN) {
        List list = new ArrayList();
        try {
            LdapContext ctx = connectLdap();
            NamingEnumeration objs = ctx.search(searchDN, null);
            Map<String, Object> map = null;
            while (objs.hasMoreElements()) {
                SearchResult match = (SearchResult) objs.nextElement();
                System.out.println(match.getName());
                Attributes attrs = match.getAttributes();
                NamingEnumeration e = attrs.getAll();
                map = new HashMap<String, Object>();
                while (e.hasMoreElements()) {
                    Attribute attr = (Attribute) e.nextElement();
                    System.out.println(attr.getID());
                    if (attr.size() > 1) {
                        Object[] objclass = new String[attr.size()];
                        for (int i = 0; i < attr.size(); i++) {
                            objclass[i] = attr.get(i);
                        }
                        map.put(attr.getID(), objclass);
                    } else {
                        map.put(attr.getID(), attr.get(0));
                    }
                }
                list.add(map);
            }
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 根据baseDN查询某一个用户
     *
     * @param ldapContext ldap连接信息
     * @param baseDN      dn
     * @return 用户列表
     */
    public static List<LdapUser> readLdap(LdapContext ldapContext, String baseDN) {
        List<LdapUser> ldapUserList = new ArrayList<>();
        if (ldapContext != null) {
            // 过滤条件
            String filter = "(objectClass=posixAccount)";
            String[] POSIX_ACCOUNT = {"dn", "cn", "sn", "uid", "uidNumber", "gidNumber", "userPassword", "displayName", "mail", "description", "loginShell", "homeDirectory", "Telephone"};
            SearchControls searchControls = new SearchControls();   // 搜索控件
            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            searchControls.setReturningAttributes(POSIX_ACCOUNT);
            try {
                NamingEnumeration<SearchResult> answer = ldapContext.search(baseDN, filter, searchControls);
                while (answer.hasMore()) {
                    SearchResult result = answer.next();
                    NamingEnumeration<? extends Attribute> attrs = result.getAttributes().getAll();
                    LdapUser ldapUser = new LdapUser();
                    while (attrs.hasMore()) {
                        Attribute attr = attrs.next();
                        if ("cn".equals(attr.getID())) {
                            ldapUser.setCn(attr.get().toString());
                        } else if ("sn".equals(attr.getID())) {
                            ldapUser.setSn(attr.get().toString());
                        } else if ("userPassword".equals(attr.getID())) {
                            Object passwordValue = attr.get();
                            ldapUser.setUserPassword(new String((byte[]) passwordValue));
                        } else if ("displayName".equals(attr.getID())) {
                            ldapUser.setDisplayName(attr.get().toString());
                        } else if ("uid".equals(attr.getID())) {
                            ldapUser.setUid(attr.get().toString());
                        } else if ("uidNumber".equals(attr.getID())) {
                            ldapUser.setUidNumber(attr.get().toString());
                        } else if ("gidNumber".equals(attr.getID())) {
                            ldapUser.setGidNumber(attr.get().toString());
                        } else if ("loginShell".equals(attr.getID())) {
                            ldapUser.setLoginShell(attr.get().toString());
                        } else if ("homeDirectory".equals(attr.getID())) {
                            ldapUser.setHomeDirectory(attr.get().toString());
                        } else if ("Telephone".equals(attr.getID())) {
                            ldapUser.setTelephone(attr.get().toString());
                        } else if ("description".equals(attr.getID())) {
                            ldapUser.setDescription(attr.get().toString());
                        }
                    }
                    if (ldapUser.getUid() != null) {
                        ldapUserList.add(ldapUser);
                    }
                }
            } catch (NamingException e) {
                throw new RuntimeException(e);
            }
        }
        return ldapUserList;
    }


    /**
     * 添加组
     *
     * @param ldapUser    // 需要添加的组信息
     * @param ldapContext // LDAP连接信息
     * @return
     */
    public static boolean addGroups(LdapUser ldapUser, LdapContext ldapContext) {
        BasicAttributes basicAttributes = new BasicAttributes();
        BasicAttribute objectClassSet = new BasicAttribute("objectClass");
        objectClassSet.add("posixGroup");
        objectClassSet.add("top");
        basicAttributes.put(objectClassSet);
        basicAttributes.put("cn", ldapUser.getCn());
        basicAttributes.put("userPassword", ldapUser.getUserPassword());
        basicAttributes.put("gidNumber", ldapUser.getGidNumber());
        basicAttributes.put("memberUid", ldapUser.getCn());
        String dn = "cn=" + ldapUser.getCn() + ",dc=huada,dc=pcm";
        System.out.println("添加组开始：" + dn);
        try {
            ldapContext.createSubcontext(dn, basicAttributes);
            System.out.println("添加组成功：" + dn);
            return true;
        } catch (NamingException e) {
            System.out.println("添加组失败：" + dn);
            throw new RuntimeException(e);
        }
    }

    /**
     * 添加用户
     *
     * @param userMap 要添加的用户信息
     */
    @Deprecated
    public static void addUser1(HashMap<String, String> userMap) {
        LdapContext ldapContext = connectLdap();// 建立ldap连接
        try {
            String name = userMap.get("name");
            String email = userMap.get("email");
            String sn = name.substring(0, 1);
            String givenName = name.substring(1);
            String uid = userMap.get("uid");
            String title = userMap.get("title");

            BasicAttributes attris = new BasicAttributes();
            BasicAttribute objectclassSet = new BasicAttribute("objectclass");
            objectclassSet.add("inetOrgPerson");
            objectclassSet.add("posixAccount");
            // objectclassSet.add("shadowAccount");
            objectclassSet.add("top");

            attris.put(objectclassSet);
            // attris.put(new BasicAttribute("nsaccountlock", "true"));// 刚添加到freeipa的用户为禁用状态(我这里添加这句话会报错)
            attris.put("sn", sn);
            attris.put("givenName", givenName);
            attris.put("cn", name);
            attris.put("uid", uid);
            attris.put("userPassword", "abc@123456");// 初始密码为abc@123456
            attris.put("homeDirectory", "/home/" + uid);// 主目录
            attris.put("uidNumber", userMap.get("uidNumber"));// 用户号
            attris.put("gidNumber", userMap.get("gidNumber"));// 部门号
            attris.put("telephoneNumber", userMap.get("mobile"));// 手机号
            attris.put("title", title);// 职称

            ldapContext.createSubcontext("uid=" + uid + "," + "cn=shggroup4,dc=huada,dc=pcm", attris); // userRoot是用户所属的目录
            System.out.println("添加用户---" + uid + "---成功");
        } catch (Exception e) {
            e.printStackTrace();
            return;
        } finally {
            if (ldapContext != null) close();// 关闭ldap连接
        }
    }

    /**
     * 添加用户
     *
     * @param ldapUser    要添加的用户信息
     * @param ldapContext ldap连接信息
     * @return
     */
    public static boolean addUser2(LdapUser ldapUser, LdapContext ldapContext) {

        BasicAttributes basicAttributes = new BasicAttributes();
        BasicAttribute objectClassSet = new BasicAttribute("objectClass");
        objectClassSet.add("posixAccount");
        objectClassSet.add("inetOrgPerson");
        objectClassSet.add("top");
        // objectClassSet.add("shadowAccount");// 这个是什么意思？
        basicAttributes.put(objectClassSet);
        basicAttributes.put("cn", ldapUser.getCn());
        basicAttributes.put("sn", ldapUser.getSn());
        basicAttributes.put("uid", ldapUser.getUid());
        basicAttributes.put("uidNumber", ldapUser.getUidNumber());
        basicAttributes.put("userPassword", ldapUser.getUserPassword());
        basicAttributes.put("gidNumber", ldapUser.getGidNumber());
        basicAttributes.put("homeDirectory", "/home/" + ldapUser.getCn());
        basicAttributes.put("loginShell", "/bin/bash");
        String dn = "uid=" + ldapUser.getUid() + ",cn=shggroup4,dc=huada,dc=pcm";
        System.out.println("添加用户开始：" + dn);
        try {
            ldapContext.createSubcontext(dn, basicAttributes);
            System.out.println("添加用户成功：" + dn);
            return true;
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 添加属性
     *
     * @param ldapUser    // 要修改的用户信息
     * @param ldapContext // ldap连接信息
     * @return
     */
    public static boolean modifyInformationOfAddAttribute(LdapUser ldapUser, LdapContext ldapContext) {
        ModificationItem[] mods = new ModificationItem[1];
        String dn = "uid=" + ldapUser.getUid() + ",cn=shggroup4,dc=huada,dc=pcm";
        // 添加属性测试
        BasicAttribute attr0 = new BasicAttribute("description", "测试添加属性");
        mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr0);
        try {
            ldapContext.modifyAttributes(dn, mods);
            System.out.println("添加改属性成功：" + dn);
            return true;
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 修改密码
     *
     * @param ldapUser    // 要修改的用户信息
     * @param ldapContext // ldap连接信息
     * @return
     */
    public static boolean modifyInformationOfModifyAttribute(LdapUser ldapUser, LdapContext ldapContext) {
        ModificationItem[] mods = new ModificationItem[1];
        String dn = "uid=" + ldapUser.getUid() + ",cn=shggroup4,dc=huada,dc=pcm";
        BasicAttribute attr0 = new BasicAttribute("userPassword", ldapUser.getUserPassword());
        mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr0);
        try {
            ldapContext.modifyAttributes(dn,mods);
            System.out.println("修改密码属性成功：" + dn);
            return true;
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除属性
     *
     * @param ldapUser    // 要修改的用户信息
     * @param ldapContext // ldap连接信息
     * @return
     */
    public static boolean modifyInformationOfDeletedAttribute(LdapUser ldapUser, LdapContext ldapContext) {
        ModificationItem[] mods = new ModificationItem[1];
        String dn = "uid=" + ldapUser.getUid() + ",cn=shggroup4,dc=huada,dc=pcm";
        BasicAttribute attr0 = new BasicAttribute("description", ldapUser.getDescription());
        mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr0);
        try {
            ldapContext.modifyAttributes(dn,mods);
            System.out.println("修改密码属性成功：" + dn);
            return true;
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean lookupLdapUser(LdapContext ldapContext,String name){
        try {
            Object shggroup4 = ldapContext.lookup(name);
            if(shggroup4 != null){
                return true;
            }
        } catch (NamingException e) {
            return false;
        }
        return false;
    }
    public static void main(String[] args) {
        LdapContext ldapContext = connectLdap();

        boolean shggroup4 = lookupLdapUser(ldapContext, "cn=shg001,dc=huada,dc=pcm");
        System.out.println("shggroup4 = " + shggroup4);


        // 针对modifyInformation方法测试
        // 1. 首先查询出要修改的用户
        String baseDN = "cn=shg001,dc=huada,dc=pcm";
        List<LdapUser> ldapUsers = readLdap(ldapContext, baseDN);
        System.out.println("ldapUsers = " + ldapUsers.get(0));
        // ldapUsers.get(0).setUserPassword("123456");
        // System.out.println(ldapUsers);

        // 2. 调用修改方法，添加一个属性
        // modifyInformationOfAddAttribute(ldapUsers.get(0), ldapContext);

        // 3. 测试修改密码
        // modifyInformationOfModifyAttribute(ldapUsers.get(0),ldapContext);

        // 4. 测试删除属性
        // modifyInformationOfDeletedAttribute(ldapUsers.get(0), ldapContext);

        // 针对addGroups方法测试
        LdapUser ldapUser2 = new LdapUser();
        ldapUser2.setCn("shggroup5");
        ldapUser2.setGidNumber("507");
        ldapUser2.setUserPassword("abc@123456");
        // addGroups(ldapUser2, ldapContext);
        System.out.println("---------");

        // 针对addUser2方法测试
        LdapUser ldapUser1 = new LdapUser();
        ldapUser1.setCn("shg5");
        ldapUser1.setSn("sun");
        ldapUser1.setUid("shg");
        ldapUser1.setUidNumber("2023");
        ldapUser1.setUserPassword("abc@123456");
        ldapUser1.setGidNumber("506");
        // addUser2(ldapUser1, ldapContext);
        System.out.println("---------");

        // 针对addUser1方法测试
        HashMap<String, String> userMap = new HashMap<>();
        userMap.put("name", "xuxiaoqing");
        userMap.put("email", "123456@qq.com");
        userMap.put("uid", "xxu");
        userMap.put("title", "Test Title");
        userMap.put("uidNumber", "2022");
        userMap.put("gidNumber", "506");
        userMap.put("mobile", "18170628920");
        // addUser1(userMap);
        System.out.println("---------");

        // 针对findByDN2方法测试
        // List byDN2 = findByDN2(searchDN);
        // System.out.println("byDN2 = " + byDN2);
        // System.out.println("---------");

        // 针对findByDN1方法测试
        // String searchDN = " dc=huada,dc=pcm";                // objectClass = dcObject / organizationalUnit
        // String searchDN = "cn=shggroup4,dc=huada,dc=pcm";    // objectClass = posixGroup / top
        // String searchDN = "cn=shga5.shga5,dc=huada,dc=pcm";     // objectClass = inetOrgPerson / posixAccount / top
        // findByDN1(searchDN);
        // System.out.println("---------");

    }
}
